﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MacomberMapSystem.Common.Serialization;
using System.Data.Common;
using System.Data.SQLite;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.User_Interfaces.CIM_Browser;
using MacomberMapSystem.Common.Integration;
using MacomberMapSystem.Common.Internals;
using System.IO;
using System.Data;
using System.Xml;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using System.Drawing;
using System.Threading;
using Ionic.Zip;
using MacomberMapSystem.Common.CIM.Direct;
using MacomberMapSystem.Common.Exports;
using MacomberMapSystem.Common.User_Interfaces.Database;

namespace MacomberMapSystem.Common.Database
{
    /// <summary>
    /// This class holds information on a network model
    /// </summary>
    public class MM_Database_Model : MM_Repository, IComparable<MM_Database_Model>, IComparable
    {
        #region Variable declarations  - in database
        /// <summary>The name of the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String Name;

        /// <summary>The model name w/o extension</summary>
        public String NameWithoutExtension
        {
            get { return Path.GetFileNameWithoutExtension(Name); }
        }

        /// <summary>The full path (including the server name) to the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String FullPath;

        /// <summary>The alternate path (including the server name) to the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String AlternatePath;

        /// <summary>The class of the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String ModelClass;

        /// <summary>The full class of the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String FullClass;

        /// <summary>When the model was exported</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public DateTime ExportedOn;

        /// <summary>When the model's validity start period begins</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public DateTime ValidStart;

        /// <summary>The previous model replaced by the current one</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public MM_Database_Model PriorModel;

        /// <summary>The number of elements in the database</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public int ElementCount;

        /// <summary>The number of TEIDs in the database</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public int TEIDCount;

        /// <summary>The largest TEID within the model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public UInt64 MaxTEID;

        /// <summary>The largest available mRID value</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public Int32 MaxMRID = 0;

        /// <summary>The number of added elements since the prior model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public int Comparison_Additions;

        /// <summary>The number of changed elements since the prior model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public int Comparison_Changes;

        /// <summary>The number of deleted elements since the prior model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public int Comparison_Deletions;

        /// <summary>The time of the model being posted to sharepoint</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public DateTime PostedOn = DateTime.FromOADate(0);

        /// <summary>The time of the model validation starting</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public DateTime ValidationStart = DateTime.FromOADate(0);

        /// <summary>The time of the model validation/export ending</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public DateTime ValidationEnd = DateTime.FromOADate(0);

        /// <summary>The user who kicked off the validation process</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public MM_Database_User ValidationBy = null;

        /// <summary>The user who posted the one-lines to sharepoint</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public MM_Database_User PostedBy = null;

        /// <summary>The number of one-lines that were properly exported</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public Int32 ExportedNum = 0;

        /// <summary>The number of one-lines that should be exported in a proper output</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public Int32 IdealNum = 0;

        /// <summary>The category of our model</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String ModelCategory;

        /// <summary>The target sharepoint folder for our files</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue)]
        public String TargetSPFolder;

        /// <summary>The repository to which parameters should be passed</summary>
        public MM_Repository Repository;

        /// <summary>Our tracking of model location</summary>
        public enum enumModelLocations
        {
            /// <summary>Our model location is unknown</summary>
            Unknown,
            /// <summary>The model is local, in our index directory</summary>
            InIndex,
            /// <summary>The model is local, in our primary directory</summary>
            InPrimary,
            /// <summary>The model is local, in our alternate directory</summary>
            InAlternate
        }


        /// <summary>The location of our model</summary>
        public enumModelLocations ModelLocation;

        /// <summary>The path to our model</summary>
        public String ModelFullPath
        {
            get
            {
                if (ModelLocation == enumModelLocations.InIndex)
                    return Db.ModelIndexPath;
                else if (ModelLocation == enumModelLocations.InPrimary)
                    return FullPath;
                else if (ModelLocation == enumModelLocations.InAlternate)
                    return AlternatePath;
                else
                    throw new InvalidOperationException("Unknown model location.");
            }
        }

        /// <summary>
        /// The collection of current model states
        /// </summary>
        public enum enumModelState
        {
            /// <summary>The model hasn't been opened</summary>
            Unopened,
            /// <summary>The model's opening has been started</summary>
            Open_Commencing,
            /// <summary>The model's index file is being copied locally</summary>
            Copying_Index,
            /// <summary>The model's index file is being decompressed locally</summary>
            Decompressing_Index,
            /// <summary>Opening the file into memory</summary>
            Opening_File,
            /// <summary>The model's attributes and type linkages are being loaded </summary>
            Loading_Attributes_And_Type_Linkages,
            /// <summary>The model's attributes are being loaded</summary>
            Loading_Attributes,
            /// <summary>The model's types are being loaded</summary>
            Loading_Types,
            /// <summary>The model's companies are being loaded</summary>
            Loading_Companies,
            /// <summary>The model's substations are being loaded</summary>
            Loading_Substations,
            /// <summary>The model's breaker-to-breaker traces are being loaded</summary>
            Loading_Traces,
            /// <summary>The model's lines are being loaded</summary>
            Loading_Lines,
            /// <summary>The model's units are being loaded</summary>
            Loading_Units,
            /// <summary>The model's loads are being loaded</summary>
            Loading_Loads,
            /// <summary>The model's transformers are being loaded</summary>
            Loading_Transformers,
            /// <summary>The model's reactive devices are being loaded</summary>            
            Loading_Reactive_Devices,
            /// <summary>The model's busbars are being loaded</summary>
            Loading_Busbars,
            /// <summary>The model load is completed</summary>
            Completed,
            /// <summary>The index is being exported</summary>
            ExportingIndex,
            /// <summary>The PUNs are being traced</summary>
            Tracing_PUNs,
            /// <summary>An error occurred.</summary>
            Error
        }

        /// <summary>
        /// Retrieve the Macomber Map model for our component
        /// </summary>
        public String MMModel {
            get
            {
                using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT MMModel FROM MM_DATABASE_MODEL WHERE ID=" + ID.ToString()))
                using (DbDataReader dRd = dCmd.ExecuteReader())
                    while (dRd.Read())
                        return dRd.GetString(0);                                            
                return "";
            }
        }


        /// <summary>When the load process was started</summary>
        public DateTime StartTime;

        /// <summary>The current state of our model</summary>
        private enumModelState _State = enumModelState.Unopened;

        /// <summary>The current state of our model</summary>
        public enumModelState State
        {
            get { return _State; }
            set
            {
                if (StateChanged != null)
                    StateChanged(this, value, _State);
                _State = value;
            }
        }

        /// <summary>
        /// The delegate for a state change
        /// </summary>
        /// <param name="Sender"></param>
        /// <param name="CurrentState"></param>
        /// <param name="PriorState"></param>
        public delegate void StateChangedDelegate(MM_Database_Model Sender, enumModelState CurrentState, enumModelState PriorState);

        /// <summary>Our event for a state change</summary>
        public event StateChangedDelegate StateChanged;

        /// <summary>The percentage of the current step</summary>
        public float StepProgress
        {
            get
            {
                if (Total == 0)
                    return 0;
                else
                    return Current / Total;
            }
        }

        /// <summary>Our current step being processed</summary>
        public float Current = 0;

        /// <summary>Our total number of items to be processed</summary>
        public float Total = 0;
        #endregion

        #region Variable declarations - static
        /// <summary>Whether the data dictionary is initialized</summary>
        private static bool DataDictionaryInitialized = false;

        /// <summary>Our XML data dictionary from CIM</summary>
        public static XmlDocument xDataDictionary = new XmlDocument();

        /// <summary>Our linkages from element TEID to trace</summary>
        public Dictionary<CIM_RdfID, CIM_Trace[]> TraceLinkages = new Dictionary<CIM_RdfID, CIM_Trace[]>();

        /// <summary>Our list of reserved trace names</summary>
        public List<String> ReservedTraceNames = new List<string>();
        #endregion

        #region Variable declarations - model only
#if !NoCacheing
        /// <summary>Our cache of one-lines</summary>
        private Dictionary<MM_Database_OneLine, MM_Oneline_Export> CachedOneLines = new Dictionary<MM_Database_OneLine, MM_Oneline_Export>(10);

        /// <summary>Our collection of CIM elements/attributes that we often retrieve</summary>
        public Dictionary<CIM_Element, Dictionary<String, String>> ElementAttributes = new Dictionary<CIM_Element, Dictionary<string, string>>();

        /// <summary>Our queue of CIM elements/attributes that we retrieve</summary>
        public Queue<CIM_Element> ElementQueue = new Queue<CIM_Element>(ElementQueueCapacity);

        /// <summary>Our queue capacity</summary>
        public static int ElementQueueCapacity = 1000;


#endif
        ///<summary>The cached elements for our testing</summary>
        public Dictionary<CIM_RdfID, CIM_Element> CachedElements = null;

        /// <summary>Our collection of elements in PUNs</summary>
        public Dictionary<CIM_RdfID, CIM_Element> PUNElements = new Dictionary<CIM_RdfID, CIM_Element>();

        /// <summary>Our collection of stations with PUNs</summary>
        public List<CIM_Element> PUNStations = new List<CIM_Element>();

        /// <summary>Whether the model is direct</summary>
        public bool DirectModel = false;

        /// <summary>The connection to the SQLite index</summary>
        public SQLiteConnection sConn;

        /// <summary>The collection of types</summary>
        public List<string> Types = new List<string>();

        /// <summary>The collection of types</summary>
        public List<string> Attributes = new List<string>();

        /// <summary>Our collection of attributes by type</summary>
        public List<UInt16[]> AttributesByType;

        /// <summary>Our collection of attributes by type</summary>
        public List<Byte[]> TypesByAttribute;

        /// <summary>Our namespace manager for reading XML elements</summary>
        public XmlNamespaceManager nsMgr;

        /// <summary>The collection of element types</summary>
        public MM_Indexed_Dictionary<String, MM_Element_Type> ElemTypes = new MM_Indexed_Dictionary<string, MM_Element_Type>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>The default target for CIM browser requests</summary>
        public static ctlCIMBrowser DefaultTarget = null;

        /// <summary>The collection of weather and load zones</summary>
        public MM_Indexed_Dictionary<string, CIM_Element> Zones = new MM_Indexed_Dictionary<string, CIM_Element>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>Our collection of substations</summary>
        public SortedDictionary<String, CIM_Element> Substations = new SortedDictionary<string, CIM_Element>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>Our collection of companies with associated acronyms</summary>
        public SortedDictionary<String, CIM_Element> Companies = new SortedDictionary<string, CIM_Element>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>Our collection of lines</summary>
        public SortedDictionary<String, CIM_Element> Lines = new SortedDictionary<string, CIM_Element>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>Our collection of breaker-to-breaker traces</summary>
        public MM_Indexed_Dictionary<String, CIM_Trace> Traces = new MM_Indexed_Dictionary<string, CIM_Trace>(StringComparer.CurrentCultureIgnoreCase);

        /// <summary>Our collection of model changes</summary>
        public Dictionary<CIM_RdfID, CIM_Element_Difference> ModelChanges = new Dictionary<CIM_RdfID, CIM_Element_Difference>();

        /// <summary>Report the full CIM file name</summary>
        public string CIMFileName
        {
            get { return Path.Combine(this.ModelFullPath, this.Name + ".xml"); }
        }

        /// <summary>The index is in the temp folder, and should be deleted on shutdown</summary>
        private bool IndexInTemp = false;
        #endregion


        #region Initialization - class
        /// <summary>Initialize a new model</summary>
        public MM_Database_Model()
        {
            if (!DataDictionaryInitialized)
            {
                DataDictionaryInitialized = true;
                if (File.Exists("CIM_Data_Dictionary.xml"))
                    xDataDictionary.Load("CIM_Data_Dictionary.xml");
                else
                    xDataDictionary.LoadXml(Properties.Resources.CIM_Data_Dictionary);
            }
        }

        /// <summary>
        /// Initialize a new network model holder
        /// </summary>
        /// <param name="dRd"></param>
        /// <param name="Repository">The data repository</param>
        public MM_Database_Model(DbDataReader dRd, MM_Repository Repository)
            : base(dRd, Repository)
        {
            this.Repository = Repository;
            this.Db = Repository.Db;
            //Ensure our model is loaded
            if (!DataDictionaryInitialized)
            {
                DataDictionaryInitialized = true;
                if (File.Exists("CIM_Data_Dictionary.xml"))
                    xDataDictionary.Load("CIM_Data_Dictionary.xml");
                else
                    xDataDictionary.LoadXml(Properties.Resources.CIM_Data_Dictionary);
            }
        }

        /// <summary>
        /// Report the name of the model
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return ValidStart.ToString("MM/dd/yyyy") + " - " + ModelCategory + " - " + Name + (this is CIM_DirectModel ? " (direct)" : "");
        }
        #endregion

        #region Destruction - model
        /// <summary>
        /// Handle the model shutting down by closing the database
        /// </summary>
        ~MM_Database_Model()
        {
            if (sConn != null)
            {
                //Close the database, and delete the temp file
                String TempFile = sConn.ConnectionString.Split('=')[1];
                sConn.Close();
                sConn.Dispose();
                if (IndexInTemp)
                    File.Delete(TempFile);
            }
        }
        #endregion


        #region Initialization - model
        /// <summary>
        /// Open our CIM index
        /// </summary>
        public void LoadCimIndex()
        {
            State = enumModelState.Open_Commencing;
            ThreadPool.QueueUserWorkItem(OpenModel, ".CimIndex");
        }

        /// <summary>
        /// Initiate the loading of a new model
        /// </summary>
        public void InitiateLoading()
        {
            State = enumModelState.Open_Commencing;
            ThreadPool.QueueUserWorkItem(OpenModel, ".SQLite");
        }

        /// <summary>
        /// Perform the actual work to open the model
        /// </summary>
        /// <param name="state"></param>
        private void OpenModel(object state)
        {
            String Suffix = (string)state;
            IndexInTemp = String.IsNullOrEmpty(Db.ModelIndexPath);

            //First, check to see if our index exists, and if not, ask to create            
            if (!Directory.Exists(Db.ModelIndexPath))
                if (MessageBox.Show("The index path " + Db.ModelIndexPath + ", as specified in the configuration file, doesn't exist. Create?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    Directory.CreateDirectory(Db.ModelIndexPath);
                else
                {
                    State = enumModelState.Error;
                    return;
                }

            //Try and see if our primary or alternate path is needed
            try
            {
                //Determine where our model is located
                if (IndexInTemp && File.Exists(Path.Combine(Db.ModelIndexPath, this.Name + ".xml")))
                    ModelLocation = enumModelLocations.InIndex;
                else if (new DirectoryInfo(this.FullPath).Exists)
                    ModelLocation = enumModelLocations.InPrimary;
                else if (!String.IsNullOrEmpty(AlternatePath) && new DirectoryInfo(this.AlternatePath).Exists)
                    ModelLocation = enumModelLocations.InAlternate;
                else
                {
                    State = enumModelState.Error;
                    MessageBox.Show("Unable to locate the model or its index!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
            catch (Exception ex)
            {
                if (new DirectoryInfo(this.AlternatePath).Exists)
                    ModelLocation = enumModelLocations.InAlternate;
                else
                {
                    State = enumModelState.Error;
                    MessageBox.Show("Error for locating the model: " + ex.Message + "\n\n" + ex.StackTrace, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            String TargetDb = String.IsNullOrEmpty(Db.ModelIndexPath) ? Path.GetTempFileName() : Path.Combine(Db.ModelIndexPath, Path.GetFileNameWithoutExtension(this.Name) + Suffix);
            String FileBase = Path.Combine(ModelFullPath, Name);





            try
            {
                //First determine whether the model is at the primary or alternate site.
                String TargetDbSource;
                if (File.Exists(FileBase + Suffix))
                {
                    TargetDbSource = FileBase + Suffix;
                    ModelLocation = enumModelLocations.InPrimary;
                }
                else if (!String.IsNullOrEmpty(AlternatePath) && File.Exists(Path.Combine(AlternatePath, Name + Suffix)))
                {
                    ModelLocation = enumModelLocations.InAlternate;
                    TargetDbSource = AlternatePath;
                }
                else
                {
                    State = enumModelState.Error;
                    MessageBox.Show("Unable to locate the index file for the model!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //Now, check our time/date stamps
                FileInfo InternalIndex = new FileInfo(TargetDb);
                FileInfo ExternalIndex = new FileInfo(TargetDbSource);
                bool CopyFile = false;
                if (!File.Exists(TargetDb))
                    CopyFile = true;
                else if (InternalIndex.LastWriteTime != ExternalIndex.LastWriteTime || InternalIndex.Length != ExternalIndex.Length)
                    CopyFile = MessageBox.Show("The index file for " + Path.GetFileNameWithoutExtension(InternalIndex.Name) + " differs between the local and server versions.\n\nReplace the\n  local (" + InternalIndex.LastWriteTime.ToString() + " / " + InternalIndex.Length.ToString("#,##0") + " bytes) with\n  other (" + ExternalIndex.LastWriteTime.ToString() + "/" + ExternalIndex.Length.ToString("#,##0") + " bytes)?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;

                if (CopyFile)
                {
                    State = enumModelState.Copying_Index;
                    this.Current = 0;
                    this.Total = ExternalIndex.Length;
                    byte[] Buffer = new byte[1024];
                    int ReadBytes;
                    using (FileStream fRead = new FileStream(TargetDbSource, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    using (FileStream fWrite = new FileStream(TargetDb, FileMode.Create))
                        while ((ReadBytes = fRead.Read(Buffer, 0, Buffer.Length)) != 0)
                        {
                            fWrite.Write(Buffer, 0, Buffer.Length);
                            Current += ReadBytes;
                        }
                    InternalIndex.LastWriteTime = ExternalIndex.LastWriteTime;
                }
            }
            catch (Exception ex)
            {
                State = enumModelState.Error;
                MessageBox.Show("Error opening model: " + MM_System_Interface.ConvertError(ex), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;

            }


            //Now, open our index file
            try
            {
                State = enumModelState.Opening_File;
                sConn = new SQLiteConnection("Data Source=" + TargetDb);
                sConn.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error opening the model's SQLite database: " + ex.Message + "\n\n" + ex.StackTrace, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                State = enumModelState.Unopened;
            }

            //Now, retrieve our relevant infomration
            try
            {
                LoadAttributesAndTypesLinkages();
                LoadAttributes();
                LoadTypes();
                LoadCompanies();
                LoadSubstationsFromCIM();
                LoadLinesFromCIM();
                LoadTraces();
                LoadPUNs();
                this.State = enumModelState.Completed;
            }
            catch (Exception ex)
            {
                this.State = enumModelState.Error;
                MessageBox.Show("Error opening model: " + ex.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Ensure a model is opened.
        /// </summary>
        /// <returns></returns>
        public bool EnsureOpened(MM_Login_Process Process = null)
        {
            //First, make sure our selected model is opened
            if (State == MM_Database_Model.enumModelState.Unopened || State == enumModelState.Error)
                if (!Thread.CurrentThread.IsBackground && MessageBox.Show("Open model " + Name + "?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                    return false;
                else
                    try
                    {
                        InitiateLoading();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Unable to open model: " + MM_System_Interface.ConvertError(ex), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return false;
                    }

            while (State != MM_Database_Model.enumModelState.Completed)
            {
                Thread.Sleep(250);
                Application.DoEvents();
                if (Process != null)
                {
                    Process.Title = State.ToString();
                    Process.TaskTotal = (int)Total;
                    Process.TaskCurrent = (int)Current;
                }
                if (State == enumModelState.Error)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Handle reporting ZIP extract progress
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
        {
            Current = (float)e.BytesTransferred;
            Total = (float)e.TotalBytesToTransfer;
        }



        /// <summary>
        /// Retrieve our list of element checksums
        /// </summary>
        /// <returns></returns>
        public virtual Dictionary<CIM_RdfID, byte[]> GetElementChecksums()
        {
            Dictionary<CIM_RdfID, byte[]> ElemInfo = new Dictionary<CIM_RdfID, byte[]>();
            using (DbCommand dCmd = sConn.CreateCommand())
            {
                dCmd.CommandText = "Select [rdfID],[Checksum] FROM CIM_Elements";
                using (DbDataReader dRd = dCmd.ExecuteReader())
                    while (dRd.Read())
                        if (dRd["Checksum"] is byte[])
                            ElemInfo.Add(new CIM_RdfID((byte[])dRd["rdfID"]), (byte[])dRd["Checksum"]);
            }
            return ElemInfo;
        }


        /// <summary>
        /// Load our collection of lines
        /// </summary>
        public void LoadLinesFromCIM()
        {
            try
            {

                this.State = enumModelState.Loading_Lines;
                CIM_Element[] Elems = ElementsOfType("cim:ACLineSegment");
                this.Current = 0;
                this.Total = Elems.Length;
                foreach (CIM_Element Line in Elems)
                    try
                    {
                        this.Current++;
                        Lines.Add(TitleCase(Line.LineName), Line);
                    }
                    catch
                    { }
            }
            catch
            { }
        }

        /// <summary>
        /// Load our collection of substations
        /// </summary>
        public void LoadSubstationsFromCIM()
        {
            this.State = enumModelState.Loading_Substations;
            CIM_Element[] Elems = ElementsOfType("cim:Substation");
            this.Current = 0;
            this.Total = Elems.Length;
            foreach (CIM_Element Sub in Elems)
            {
                this.Current++;
                Substations.Add(TitleCase(Sub.Name), Sub);
            }
        }

        /// <summary>
        /// Load our collection of companies
        /// </summary>
        public void LoadCompanies()
        {
            this.State = enumModelState.Loading_Companies;
            CIM_Element[] Elems = ElementsOfType("cim:Company");
            this.Current = 0;
            this.Total = Elems.Length;
            foreach (CIM_Element Company in Elems)
            {
                this.Current++;
                Companies.Add(TitleCase(Company.Attributes["cim:IdentifiedObject.aliasName"]), Company);
            }
        }

        /// <summary>
        /// Load our collection of contingencies
        /// </summary>
        public void LoadTraces()
        {
            this.State = enumModelState.Loading_Traces;

            //Pull in our contingency information, whether from the SQLite index, b2b document, etc.
            Dictionary<int, String> TraceIDs = new Dictionary<int, string>();
            Dictionary<UInt64, List<CIM_Trace>> TEIDTraces = new Dictionary<UInt64, List<CIM_Trace>>();

            //First, make sure we have an SQLite connection

            if (sConn == null)
            {
                sConn = new SQLiteConnection("Data Source=" + Path.Combine(ModelFullPath, Name + ".SQLite"));
                sConn.Open();
            }

            try
            {
                using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Traces", sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                        Traces.Add(Convert.ToInt32(sRd["ID"]), sRd["Name"].ToString(), new CIM_Trace(sRd, this));

                //Pull in our trace elements
                Dictionary<int, List<CIM_RdfID>> TracerdfIDs = new Dictionary<int, List<CIM_RdfID>>();
                using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Trace_Elements", sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                    {
                        List<CIM_RdfID> FoundID;
                        if (!TracerdfIDs.TryGetValue(Convert.ToInt32(sRd["TraceID"]), out FoundID))
                            TracerdfIDs.Add(Convert.ToInt32(sRd["TraceID"]), FoundID = new List<CIM_RdfID>());
                        FoundID.Add(new CIM_RdfID((byte[])sRd["rdfID"]));
                    }
                foreach (KeyValuePair<int, List<CIM_RdfID>> kvp in TracerdfIDs)
                    Traces[kvp.Key].rdfIDs = kvp.Value.ToArray();

                //Load in our reserved contingency names
                using (SQLiteCommand sCmd = new SQLiteCommand("SELECT Name FROM CIM_Reserved_Trace_Names", sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                        ReservedTraceNames.Add(sRd["Name"].ToString());
            }
            catch (Exception)
            {

            }




            //If not, open our our breaker-to-breaker information
           

            if (Traces.Count == 0)
                {
                    XmlDocument xDoc = new XmlDocument();
                    if (!File.Exists(Path.Combine(ModelFullPath, Name + "-BreakerToBreakers.xml")))
                    {
                        //Now, start writing our output file
                        using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(ModelFullPath, Name + "-BreakerToBreakers.xml"), new UTF8Encoding(false)) { Formatting = Formatting.Indented })
                        {
                            xW.WriteStartDocument();
                            xW.WriteStartElement("BreakerToBreakers");
                            foreach (CIM_Element Ctg in ElementsOfType("cim:Contingency"))
                            {
                                xW.WriteStartElement("BreakerToBreaker");
                                xW.WriteAttributeString("Name", Ctg.Name);
                                xW.WriteAttributeString("Description", Ctg.LongName);
                                xW.WriteAttributeString("Radial", "-1");
                                xW.WriteAttributeString("rdfID", Ctg.rdfID.ToString());

                                //Write our groups
                                List<string> Groups = new List<string>();
                                    foreach (CIM_Element CtgGroup in Ctg.Parse("etx:ContingencyGroupMember>etx:ContingencyGroup", this))
                                    Groups.Add(CtgGroup.Name);
                                xW.WriteAttributeString("Groups", String.Join(",", Groups.ToArray()));

                                //Go through our elements and track TEIDs, rdfIDs, substation TEID/rdfIDs.
                                List<String> TEIDs = new List<string>();
                                List<String> rdfIDs = new List<String>();
                                List<String> SubTEIDs = new List<String>();
                                List<String> SubRDFIDs = new List<String>();

                                foreach (CIM_Element Elem in Ctg.Elements)
                                {
                                    TEIDs.Add(Elem.TEID);
                                    rdfIDs.Add(Elem.rdfID.ToString());
                                    CIM_Element Sub = Elem.Substation;
                                    if (Sub != null && !SubTEIDs.Contains(Sub.TEID))
                                    {
                                        SubTEIDs.Add(Sub.TEID);
                                        SubRDFIDs.Add(Sub.rdfID.ToString());
                                    }
                                }
                                xW.WriteAttributeString("TEIDs", String.Join(",", TEIDs.ToArray()));
                                xW.WriteAttributeString("rdfIDs", String.Join(",", rdfIDs.ToArray()));
                                xW.WriteAttributeString("SubstationTEIDs", String.Join(",", SubTEIDs.ToArray()));
                                xW.WriteAttributeString("SubstationrdfIDs", String.Join(",", SubRDFIDs.ToArray()));
                                xW.WriteEndElement();
                            }
                            xW.WriteEndElement();
                            xW.WriteEndDocument();
                        }
                    }
                    
                    xDoc.Load(Path.Combine(ModelFullPath, Name + "-BreakerToBreakers.xml"));
                    Total = xDoc.DocumentElement.ChildNodes.Count;

                    //Run our check to see if we're dealing with an old MM Loader version with the bug where the model name is given instead of the b2b name.
                    List<String> CtgNames = new List<string>();
                    if (xDoc.DocumentElement.ChildNodes[0].Attributes["Name"].Value == xDoc.DocumentElement.ChildNodes[1].Attributes["Name"].Value)
                        using (SQLiteConnection sConn2 = new SQLiteConnection("Data Source=" + Path.Combine(ModelFullPath, "Contingencies.sqlite")))
                        {
                            sConn2.Open();
                            using (SQLiteCommand sCmd2 = new SQLiteCommand("SELECT [Name] from Contingencies", sConn2))
                            using (SQLiteDataReader sRd = sCmd2.ExecuteReader())
                                while (sRd.Read())
                                    CtgNames.Add(sRd[0].ToString());
                        }

                    for (Current = 0; Current < xDoc.DocumentElement.ChildNodes.Count; Current++)
                        if (xDoc.DocumentElement.ChildNodes[(int)Current].Name == "ReservedTraceName")
                            ReservedTraceNames.Add(xDoc.DocumentElement.ChildNodes[(int)Current].Attributes["Value"].Value);
                        else
                        {
                            CIM_Trace NewTrace = new CIM_Trace(xDoc.DocumentElement.ChildNodes[(int)Current] as XmlElement, this);
                            if (CtgNames.Count > 0) 
                                NewTrace.Name = CtgNames[(int)Current];
                            Traces.Add(NewTrace.Name, NewTrace);
                        }
                    //   Traces.Add(xDoc.DocumentElement.ChildNodes[(int)Current].Attributes["Name"].Value, );
                    //                foreach (XmlElement xElem in xDoc.DocumentElement.ChildNodes)
                    //                    Traces.Add(xElem.Attributes["Name"].Value, new CIM_Trace(xElem, this));

                }

            //Now, build our linkages between traces
            List<CIM_Trace> ConnectedTraces;
            Dictionary<CIM_RdfID, List<CIM_Trace>> rdfIDTraces = new Dictionary<CIM_RdfID, List<CIM_Trace>>();
            foreach (CIM_Trace NewTrace in Traces.Values)
                foreach (CIM_RdfID rdfID in NewTrace.rdfIDs)
                {
                    if (!rdfIDTraces.TryGetValue(rdfID, out ConnectedTraces))
                        rdfIDTraces.Add(rdfID, ConnectedTraces = new List<CIM_Trace>(4));
                    ConnectedTraces.Add(NewTrace);
                }
            foreach (KeyValuePair<CIM_RdfID, List<CIM_Trace>> kvp in rdfIDTraces)
                TraceLinkages.Add(kvp.Key, kvp.Value.ToArray());

        }





        /// <summary>
        /// Load our collection of types
        /// </summary>
        public void LoadTypes()
        {
            Types.Clear();

            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Types ORDER BY [ID]", sConn))
            using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                while (sRd.Read())
                    Types.Add((string)sRd["Name"]);

            Types.TrimExcess();
        }

        /// <summary>
        /// Load our collection of attributes
        /// </summary>
        public void LoadAttributes()
        {
            Attributes.Clear();
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Attributes ORDER BY [ID]", sConn))
            using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                while (sRd.Read())
                    Attributes.Add((string)sRd["Name"]);
            Attributes.TrimExcess();
        }


        /// <summary>
        /// Generate our list of PUNs
        /// </summary>
        public void LoadPUNs()
        {
            this.State = enumModelState.Tracing_PUNs;
            PUNStations.Clear();
            PUNElements.Clear();

            //First, check to see if we have our PUN file. If we don't, recreate it.
            if (File.Exists(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworks.xml")))
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworks.xml"));
                foreach (XmlElement xElem in xDoc.DocumentElement.ChildNodes)
                {
                    if (xElem.Name == "PUNStation")                                            
                        PUNStations.Add(FromRdfID(xElem.Attributes["rdfID"].Value));
                    else if (xElem.Name == "PUN")
                    {
                        CIM_Element PUNid = FromRdfID(xElem.Attributes["rdfID"].Value);
                        CIM_Element DuplicatePUN;
                        foreach (String rdfID in xElem.InnerText.Split(new char[] {','},  StringSplitOptions.RemoveEmptyEntries))
                        {
                            CIM_RdfID NewID = new CIM_RdfID(rdfID);
                            if (PUNElements.TryGetValue(NewID, out DuplicatePUN))
                                MessageBox.Show("Element with rdf:ID " + rdfID + " is in two or more PUNs: " + PUNid + " and " + DuplicatePUN.Name, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                            else
                                PUNElements.Add(NewID, PUNid);
                        }
                    }
                }
            }
            else
                try
                {
                    List<String> PUNIssues = new List<string>();
                    using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworks.xml"), Encoding.UTF8))
                    using (StreamWriter sW = new StreamWriter(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworkLineXFZBRNodeSeriesCompensator.csv"), false, new UTF8Encoding(false)))
                    using (StreamWriter sW2 = new StreamWriter(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworkLineXFZBRNodeSeriesCompensator_upgraded.csv"), false, new UTF8Encoding(false)))
                    {
                        sW.WriteLine("PUN,Type,Substation,Name,TEID");
                        sW2.WriteLine("I,PUN,TYPE,SUBSTATION,NAME,TEID");
                        xW.WriteStartDocument();
                        xW.Formatting = Formatting.Indented;
                        xW.WriteStartElement("PrivateUseNetworks");
                        foreach (CIM_Element Elem in ElementsOfType("etx:PrivateAreaNetwork"))                            
                            if (Elem["etx:PrivateAreaNetwork.networkType"].EndsWith("PrivateUseNetwork", StringComparison.CurrentCultureIgnoreCase))
                                try
                                {
                                    List<CIM_Element> Traced = new List<CIM_Element>();

                                    //Updated 9/4/12, ML as per TM's request. Now, PUNs w/ units do the tracing out to PV, and PUNS with only loads highlight the whole substation
                                    if (FoundGenForPUN(Elem))
                                    {
                                        foreach (KeyValuePair<CIM_Element, String> Elem2 in Elem.InLinksByAttributeName)
                                            if (Elem2.Key.TypeName.EndsWith("Unit", StringComparison.CurrentCultureIgnoreCase) && Elem2.Value.Contains("etx:GeneratingUnit.PrivateAreaNetwork"))
                                            {
                                                CIM_Element Unit = Elem2.Key.ParseSingle("cim:SynchronousMachine", null);
                                                if (!Traced.Contains(Unit))
                                                    Traced.Add(Unit);
                                                CIM_Trace.TraceUntilVector(Traced, Unit, false, "cim.PowerSystemsResource.contingencyComponent", 0);
                                            }
                                            else if (Elem2.Key.TypeName.EndsWith("Load", StringComparison.CurrentCultureIgnoreCase) && Elem2.Value.Contains("etx:EnergyConsumer.PrivateAreaNetwork"))
                                            {
                                                if (!Traced.Contains(Elem2.Key))
                                                    Traced.Add(Elem2.Key);
                                                CIM_Trace.TraceUntilVector(Traced, Elem2.Key, false, "cim.PowerSystemsResource.contingencyComponent", 0);
                                            }
                                    }
                                    else if (FoundLoadForPUN(Elem))
                                    {
                                        foreach (KeyValuePair<CIM_Element, String> Elem2 in Elem.InLinksByAttributeName)
                                            if (Elem2.Key.TypeName.EndsWith("Load", StringComparison.CurrentCultureIgnoreCase) && !Traced.Contains(Elem2.Key) && Elem2.Value.Contains("etx:EnergyConsumer.PrivateAreaNetwork"))
                                                foreach (CIM_Element SubElem in Elem2.Key.Substation.Elements)
                                                    if (SubElem.TypeName == "cim:PowerTransformer" || SubElem.TypeName == "cim:ConnectivityNode" || SubElem.TypeName == "cim:TransformerWinding" || SubElem.TypeName == "cim:Breaker" || SubElem.TypeName == "cim:Disconnector" || SubElem.TypeName.EndsWith("Compensator") || SubElem.TypeName.EndsWith("Load") || SubElem.TypeName == "cim:SynchronousMachine")
                                                        Traced.Add(SubElem);
                                    }
                                    else                                     
                                    {
                                        foreach (KeyValuePair<CIM_Element, String> Elem2 in Elem.InLinksByAttributeName)
                                            if (!Traced.Contains(Elem2.Key) && Elem2.Value.Contains("etx:ConductingEquipment.PrivateAreaNetwork"))
                                                Traced.Add(Elem2.Key);                                                
                                    }



                                    StringBuilder sBd = new StringBuilder();
                                    foreach (CIM_Element Elem2 in Traced)
                                        if (PUNElements.ContainsKey(Elem2.rdfID))
                                            PUNIssues.Add(Elem2.ToString() + " is found in more than one PUN: " + PUNElements[Elem2.rdfID].ToString() + " and " + Elem.ToString());
                                        else
                                        {
                                            PUNElements.Add(Elem2.rdfID, Elem);
                                            sBd.Append((sBd.Length == 0 ? "" : ",") + Elem2.rdfID.ToString());
                                            if (Elem2.Substation != null && !PUNStations.Contains(Elem2.Substation))
                                            {
                                                PUNStations.Add(Elem2.Substation);
                                                xW.WriteStartElement("PUNStation");
                                                xW.WriteAttributeString("Name", Elem2.Substation.Name);
                                                xW.WriteAttributeString("LongName", Elem2.Substation.LongName);
                                                xW.WriteAttributeString("rdfID", Elem2.Substation.rdfID.ToString());
                                                xW.WriteAttributeString("TEID", Elem2.Substation.TEID);
                                                xW.WriteEndElement();
                                            }

                                            //If our element is a line, node, ZBR, XF, write it out.\

                                            if (Elem2.TypeName == "cim:ACLineSegment")
                                            {
                                                sW.WriteLine(Elem.Name + "," + Elem2.ElemType + "," + Elem2.LineStations[0].Name + " to " + Elem2.LineStations[1].Name + "," + Elem2.LineName + "," + Elem2.TEID.ToString());
                                                sW2.WriteLine("D," + Elem.Name + "," + Elem2.ElemType + "," + Elem2.LineStations[0].Name + " to " + Elem2.LineStations[1].Name + "," + Elem2.LineName + "," + Elem2.TEID.ToString());
                                            }
                                            else if (Elem2.TypeName == "cim:PowerTransformer" || Elem2.TypeName == "cim:ConnectivityNode")
                                            {
                                                sW.WriteLine(Elem.Name + "," + Elem2.ElemType + "," + Elem2.Substation.Name + "," + Elem2.Name + "," + Elem2.TEID.ToString());
                                                sW2.WriteLine("D," + Elem.Name + "," + Elem2.ElemType + "," + Elem2.Substation.Name + "," + Elem2.Name + "," + Elem2.TEID.ToString());
                                            }
                                            else if (Elem2.TypeName == "cim:SeriesCompensator")
                                            {
                                                sW.WriteLine(Elem.Name + ",Line," + Elem2.Substation.Name + "," + Elem2.LineName.ToUpper() + "," + Elem2.TEID.ToString());
                                                sW2.WriteLine("D," + Elem.Name + ",Line," + Elem2.Substation.Name + "," + Elem2.LineName.ToUpper() + "," + Elem2.TEID.ToString());
                                            }
                                        }
                                    xW.WriteStartElement("PUN");
                                    xW.WriteAttributeString("Name", Elem.Name);
                                    xW.WriteAttributeString("rdfID", Elem.rdfID.ToString());
                                    xW.WriteValue(sBd.ToString());
                                    xW.WriteEndElement();
                                    Application.DoEvents();
                                }
                                catch
                                { }

                        xW.WriteEndElement();
                    }

                    //Perform a PUN delta file
                    using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(ModelFullPath, Name + "-PrivateUseNetwork-Deltas.xml"), Encoding.UTF8))
                    {
                        xW.Formatting = Formatting.Indented;
                        xW.WriteStartDocument();
                        xW.WriteStartElement("PUN_Delta");
                        xW.WriteAttributeString("RunOn", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified));
                        foreach (CIM_Element PUN_Elem in ElementsOfType("etx:PrivateAreaNetwork"))
                        {
                            List<CIM_RdfID> ElemsInModel = new List<CIM_RdfID>();
                            List<CIM_RdfID> ElemsInTrace = new List<CIM_RdfID>();
                            foreach (KeyValuePair<CIM_RdfID, CIM_Element> kvp in PUNElements)
                                if (kvp.Value.rdfID.Equals(PUN_Elem.rdfID))
                                    ElemsInTrace.Add(kvp.Key);
                            foreach (CIM_Link Elem in PUN_Elem.InLinks)
                                if (Elem.TypeName == "cim:ACLineSegment" || Elem.TypeName == "cim:TransformerWinding" || Elem.TypeName == "cim:Breaker" || Elem.TypeName == "cim:Disconnector" || Elem.TypeName.EndsWith("Compensator") || Elem.TypeName.EndsWith("Load") || Elem.TypeName == "cim:SynchronousMachine")
                                    ElemsInModel.Add(Elem.rdfID);

                            //Write out our information
                            xW.WriteStartElement("PUN_Information");
                            xW.WriteAttributeString("Name", PUN_Elem.Name);
                            xW.WriteAttributeString("rdfID", PUN_Elem.rdfID.ToString());
                            xW.WriteAttributeString("Traced_Elements", ElemsInTrace.Count.ToString());
                            xW.WriteAttributeString("Modeled_Elements", ElemsInModel.Count.ToString());

                            List<CIM_RdfID> MissingInModel = new List<CIM_RdfID>();
                            List<CIM_RdfID> MissingInTrace = new List<CIM_RdfID>();
                            foreach (CIM_RdfID ElemInTrace in ElemsInTrace)
                                if (!ElemsInModel.Contains(ElemInTrace))
                                    MissingInModel.Add(ElemInTrace);
                            foreach (CIM_RdfID ElemInModel in ElemsInModel)
                                if (!ElemsInTrace.Contains(ElemInModel))
                                    MissingInTrace.Add(ElemInModel);
                            {
                                CIM_Element Elem;
                                foreach (List<CIM_RdfID> ElemsToParse in new List<CIM_RdfID>[] { MissingInModel, MissingInTrace })
                                    foreach (CIM_RdfID ElemID in ElemsToParse)
                                        if ((Elem = FromRdfID(ElemID)).TypeName != "cim:ConnectivityNode" && Elem.TypeName != "cim:PowerTransformer" && Elem.TypeName != "cim:BusbarSection")
                                        {
                                            xW.WriteStartElement(ElemsToParse == MissingInModel ? "MissingInModel" : "MissingInTrace");

                                            xW.WriteAttributeString("Type", Elem.TypeName);
                                            xW.WriteAttributeString("Name", Elem.LineName);
                                            if (Elem.Substation != null)
                                                xW.WriteAttributeString("Substation", Elem.Substation.Name);
                                            if (Elem.VoltageLevel != null)
                                                xW.WriteAttributeString("Voltage", Elem.VoltageLevel.Name);
                                            xW.WriteAttributeString("TEID", Elem.TEID);
                                            xW.WriteAttributeString("rdfID", Elem.rdfID.ToString());
                                            xW.WriteEndElement();
                                        }
                            }
                            xW.WriteEndElement();
                            xW.Flush();
                        }


                        xW.WriteEndElement();
                        xW.WriteEndDocument();
                    }



                    if (PUNIssues.Count > 0)
                    {
                        using (StreamWriter sWErr = new StreamWriter(Path.Combine(ModelFullPath, Name + "-PrivateUseNetworkError.log"), false, new UTF8Encoding(false)))
                            foreach (string str in PUNIssues)
                                sWErr.WriteLine(str);
                        using (Form HighlightForm = new Form())
                            MessageBox.Show(HighlightForm, "There were " + PUNIssues.Count.ToString("#,##0") + " issues found processing the PUNs:\n\n" + String.Join("\r\n", PUNIssues.ToArray()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    }
                }
                catch (Exception ex)
                {
                    using (Form HighlightForm = new Form())
                        MessageBox.Show(HighlightForm, "Error tracing PUNs: " + ex.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
        }        

        /// <summary>
        /// Determine if a generator has been found in a PUN's incoming links
        /// </summary>
        /// <param name="Elem"></param>
        /// <returns></returns>
        private bool FoundGenForPUN(CIM_Element Elem)
        {
            foreach (CIM_Link Elem2 in Elem.InLinks)
                if (Elem2.TypeName.EndsWith("Unit"))
                    return true;
            return false;
        }

        /// <summary>
        /// Determine if a load has been found in a PUN's incoming links
        /// </summary>
        /// <param name="Elem"></param>
        /// <returns></returns>
        private bool FoundLoadForPUN(CIM_Element Elem)
        {
            foreach (CIM_Link Elem2 in Elem.InLinks)
                if (Elem2.TypeName.EndsWith("Load"))
                    return true;
            return false;
        }

        /// <summary>
        /// Load our type to attributes and attribute to types mappings
        /// </summary>
        public void LoadAttributesAndTypesLinkages()
        {
            TypesByAttribute = new List<byte[]>();
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT [Attributes] FROM CIM_TypesByAttribute ORDER BY [ID]", sConn))
            using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                while (sRd.Read())
                    TypesByAttribute.Add((byte[])sRd["Attributes"]);

            AttributesByType = new List<ushort[]>();
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT [Types] FROM CIM_AttributesByType ORDER BY [ID]", sConn))
            using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                while (sRd.Read())
                {
                    Byte[] OutByte = (Byte[])sRd["Types"];
                    UInt16[] OutIndex = new ushort[OutByte.Length / 2];
                    for (int a = 0; a < OutByte.Length; a += 2)
                        OutIndex[a / 2] = BitConverter.ToUInt16(OutByte, a);
                    AttributesByType.Add(OutIndex);
                }

            AttributesByType.TrimExcess();
            TypesByAttribute.TrimExcess();
        }
        #endregion

        #region Diagnostic cacheing
        ///<summary>
        ///Test to determine whether an element is set to be cached
        ///</summary>
        ///<param name="InElement">The incoming element</param>
        public CIM_Element TestCache(CIM_Element InElement)
        {
            if (InElement != null && InElement.CIM.CachedElements != null && !InElement.CIM.CachedElements.ContainsKey(InElement.rdfID))
                InElement.CIM.CachedElements.Add(InElement.rdfID, InElement);
            return InElement;
        }

        ///<summary>
        ///Test to determine whether an element is set to be cached
        ///</summary>
        ///<param name="InElements">The incoming element</param>
        public CIM_Element[] TestCache(CIM_Element[] InElements)
        {
            foreach (CIM_Element InElement in InElements)
                TestCache(InElement);
            return InElements;
        }
        #endregion


        #region Element locators
        /// <summary>
        /// Go through a substation's elements, and find one with the requested name
        /// </summary>
        /// <param name="SubstationName"></param>
        /// <param name="ElementName"></param>
        /// <param name="ElementType"></param>
        /// <returns></returns>
        public CIM_Element FromSubstationAndElementName(String SubstationName, String ElementName, String ElementType)
        {
            CIM_Element Substation;
            if (!Substations.TryGetValue(SubstationName, out Substation))
                return null;
            foreach (CIM_Element Elem in Substation.Elements)
                if (Elem.TypeName == ElementType && String.Equals(ElementName, Elem.Name, StringComparison.CurrentCultureIgnoreCase))
                    return TestCache(Elem);
            return null;
        }

        /// <summary>
        /// Locate an element by its name
        /// </summary>
        /// <param name="ElementType"></param>
        /// <param name="ElementName"></param>
        /// <param name="Repository">The repository that may contain additionally needed elements</param>
        /// <returns></returns>
        public CIM_Element FromElementName(string ElementType, string ElementName, MM_Repository Repository)
        {
            int TargetType = Types.IndexOf(ElementType);
            if (TargetType != -1)
                using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Elements WHERE [TYPE]=:0 AND [Name]=:1 LIMIT 0,1", sConn))
                {
                    sCmd.Prepare();
                    sCmd.Parameters.AddWithValue("0", TargetType);
                    sCmd.Parameters.AddWithValue("1", ElementName);
                    using (SQLiteDataReader sRd = sCmd.ExecuteReader(CommandBehavior.SingleRow))
                        if (sRd.Read())
                            return TestCache(new CIM_Element(sRd, this));
                }
            return null;
        }



        /// <summary>
        /// Locate a CIM element by ID
        /// </summary>
        /// <param name="ID"></param>
        /// <param name="Repository">The repository that may contain additionally needed elements</param>
        /// <returns></returns>
        public virtual CIM_Element FromID(int ID, MM_Repository Repository)
        {
            using (DbCommand sCmd = CreateCommand("SELECT * FROM CIM_Elements WHERE [ID] = :ID LIMIT 0,1", sConn))
            {
                sCmd.Prepare();
                AddParameterWithValue(sCmd, "ID", Math.Abs(ID));
                using (DbDataReader sRd = sCmd.ExecuteReader())
                    if (sRd.Read())
                        return TestCache(new CIM_Element(sRd, this));
                    else
                        return null;
            }
        }

        /// <summary>
        /// Return the rdf:ID corresponding to an element
        /// </summary>
        /// <param name="TargetID"></param>
        /// <returns></returns>
        public virtual CIM_RdfID rdfIDFromID(int TargetID)
        {
            Object OutVal;
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT [rdfID] FROM CIM_Elements WHERE [ID]=" + TargetID.ToString() + " LIMIT 0,1", sConn))
                OutVal = sCmd.ExecuteScalar();
            if (OutVal is byte[])
                return new CIM_RdfID((byte[])OutVal);
            else
                return null;
        }

        /// <summary>
        /// Locate a CIM element by rdf:ID        
        /// </summary>
        /// <param name="RdfID"></param>
        /// <returns></returns>
        public virtual CIM_Element FromRdfID(String RdfID)
        {
            return FromRdfID(new CIM_RdfID(RdfID));
        }

        /// <summary>
        /// Locate a CIM element by rdf:ID
        /// </summary>
        /// <param name="RdfID"></param>
        /// <returns></returns>
        public virtual CIM_Element FromRdfID(CIM_RdfID RdfID)
        {
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT * FROM CIM_Elements WHERE [rdfID] = :rdfID LIMIT 0,1", sConn))
            {
                sCmd.Prepare();
                sCmd.Parameters.AddWithValue("rdfID", RdfID.GetBytes());
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    if (sRd.Read())
                        return TestCache(new CIM_Element(sRd, this));
            }
            return null;
        }

        /// <summary>
        /// Locate a CIM element by TEID
        /// </summary>
        /// <param name="TEID"></param>
        /// <returns></returns>
        public virtual CIM_Element FromTEID(UInt64 TEID)
        {
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT [ID] FROM CIM_TEIDs WHERE [TEID]=:TEID LIMIT 0,1", sConn))
            {
                sCmd.Prepare();
                sCmd.Parameters.AddWithValue("TEID", (Int32)TEID);
                using (SQLiteDataReader sRd = sCmd.ExecuteReader(CommandBehavior.SingleRow))
                    if (sRd.Read())
                    {
                        int ID = Convert.ToInt32(sRd[0]);
                        sRd.Close();
                        return FromID(ID, null);
                    }
            }

            return null;
        }

        /// <summary>
        /// Count the number of elements of a particular type
        /// </summary>
        /// <param name="ElemType"></param>
        /// <returns></returns>
        public int CountType(String ElemType)
        {
            //Build our list of target types
            List<int> TargetType = new List<int>();
            for (int a = 0; a < Types.Count; a++)
                if (Types[a].EndsWith(ElemType, StringComparison.CurrentCultureIgnoreCase))
                    TargetType.Add(a);

            //Go through the elements and create our list
            if (TargetType.Count > 0)
            {
                StringBuilder sCmdText = new StringBuilder("SELECT COUNT([ID]) FROM CIM_Elements WHERE ");
                for (int a = 0; a < TargetType.Count; a++)
                    if (a == 0)
                        sCmdText.Append("[TYPE]=" + TargetType[a].ToString());
                    else
                        sCmdText.Append(" OR [TYPE]=" + TargetType[a].ToString());


                using (SQLiteCommand sCmd = new SQLiteCommand(sCmdText.ToString(), sConn))
                    return Convert.ToInt32(sCmd.ExecuteScalar());
            }
            else
                return 0;
        }


        /// <summary>
        /// Retrieve the list of names of elements of a particular type
        /// </summary>
        /// <param name="ElemType">The element type to use</param>
        /// <returns></returns>
        public string[] TypeNames(String ElemType)
        {
            //Build our list of target types
            List<int> TargetType = new List<int>();
            for (int a = 0; a < Types.Count; a++)
                if (Types[a].EndsWith(ElemType, StringComparison.CurrentCultureIgnoreCase))
                    TargetType.Add(a);

            List<String> OutNames = new List<string>();
            //Go through the elements and create our list
            if (TargetType.Count > 0)
            {
                StringBuilder sCmdText = new StringBuilder("SELECT [Name] FROM CIM_Elements WHERE ");
                for (int a = 0; a < TargetType.Count; a++)
                    if (a == 0)
                        sCmdText.Append("[TYPE]=" + TargetType[a].ToString());
                    else
                        sCmdText.Append(" OR [TYPE]=" + TargetType[a].ToString());

                using (SQLiteCommand sCmd = new SQLiteCommand(sCmdText.ToString(), sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                        OutNames.Add(sRd["Name"].ToString());
            }

            return OutNames.ToArray();
        }

      

        /// <summary>
        /// Return all CIM elements of a particular type
        /// </summary>
        /// <param name="ElemType"></param>
        /// <returns></returns>
        public virtual CIM_Element[] ElementsOfType(String ElemType)
        {
            //Build our list of target types
            List<int> TargetType = new List<int>();
            for (int a = 0; a < Types.Count; a++)
                if (Types[a].EndsWith(ElemType, StringComparison.CurrentCultureIgnoreCase))
                    TargetType.Add(a);

            List<CIM_Element> OutElems = new List<CIM_Element>();

            //Go through the elements and create our list
            if (TargetType.Count > 0)
            {
                StringBuilder sCmdText = new StringBuilder("SELECT * FROM CIM_Elements WHERE ");
                for (int a = 0; a < TargetType.Count; a++)
                    if (a == 0)
                        sCmdText.Append("[TYPE]=" + TargetType[a].ToString());
                    else
                        sCmdText.Append(" OR [TYPE]=" + TargetType[a].ToString());

                CIM_Element_Difference cDiff;
                using (SQLiteCommand sCmd = new SQLiteCommand(sCmdText.ToString(), sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                        if (!ModelChanges.TryGetValue(new CIM_RdfID(sRd["rdfID"].ToString()), out cDiff) || (cDiff.ChangeType & CIM_Element_Difference.enumChangeType.ElementDeletion) != CIM_Element_Difference.enumChangeType.ElementDeletion)
                            OutElems.Add(new CIM_Element(sRd, this));

                //Now, add in our element additions of the appropriate target type
                foreach (CIM_Element_Difference ElemDiff in ModelChanges.Values)
                    if (TargetType.Contains(ElemDiff.Type) && (ElemDiff.ChangeType & CIM_Element_Difference.enumChangeType.ElementAddition) == CIM_Element_Difference.enumChangeType.ElementAddition)
                        OutElems.Add(FromRdfID(ElemDiff.rdfID));
            }

            return OutElems.ToArray();
        }

        /// <summary>
        /// Return all CIM elements of a particular type
        /// </summary>
        /// <param name="ElemType"></param>
        /// <param name="Start"></param>
        /// <param name="Length"></param>
        /// <returns></returns>
        public CIM_Element[] ElementsOfType(String ElemType, int Start, int Length)
        {
            //Build our list of target types
            List<int> TargetType = new List<int>();
            for (int a = 0; a < Types.Count; a++)
                if (Types[a].EndsWith(ElemType, StringComparison.CurrentCultureIgnoreCase))
                    TargetType.Add(a);

            List<CIM_Element> OutElems = new List<CIM_Element>();

            //Go through the elements and create our list
            if (TargetType.Count > 0)
            {
                StringBuilder sCmdText = new StringBuilder("SELECT * FROM CIM_Elements WHERE ");
                for (int a = 0; a < TargetType.Count; a++)
                    if (a == 0)
                        sCmdText.Append("[TYPE]=" + TargetType[a].ToString());
                    else
                        sCmdText.Append(" OR [TYPE]=" + TargetType[a].ToString());
                sCmdText.Append(" LIMIT " + Start.ToString() + "," + Length.ToString());


                using (SQLiteCommand sCmd = new SQLiteCommand(sCmdText.ToString(), sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                        OutElems.Add(new CIM_Element(sRd, this));
            }

            return OutElems.ToArray();
        }
        #endregion

        #region One-Line to model linkages
        /// <summary>
        /// Locate an export for a particular one-line
        /// </summary>
        /// <param name="OneLine"></param>
        /// <returns></returns>
        public MM_Oneline_Export LocateExport(MM_Database_OneLine OneLine)
        {
#if !NoCacheing                       
            MM_Oneline_Export OutExport;

            if (CachedOneLines.TryGetValue(OneLine, out OutExport))
                return OutExport;

            if (CachedOneLines.Count >= 5)
            {
                MM_Database_OneLine[] TempOLs = CachedOneLines.Keys.ToArray<MM_Database_OneLine>();
                for (int a = 0; a < TempOLs.Length - 6; a++)
                    CachedOneLines.Remove(TempOLs[a]);                
            }
            OutExport = new MM_Oneline_Export(OneLine, this);
            CachedOneLines.Add(OneLine, OutExport);
            return OutExport;
#else
            return new MM_Oneline_Export(OneLine, this);
#endif

        }
        #endregion

        #region Menu building
        /// <summary>
        /// Add our sorted list of items added
        /// </summary>
        /// <param name="DropDownCollection"></param>
        /// <param name="Models"></param>
        /// <param name="EventHandler"></param>
        public static void AddModelMenus(ToolStripItemCollection DropDownCollection, IEnumerable<MM_Database_Model> Models, ToolStripItemClickedEventHandler EventHandler)
        {
            SortedDictionary<int, MM_Database_Model> SortedModels = new SortedDictionary<int, MM_Database_Model>();
            foreach (MM_Database_Model Model in Models)
                if (Model != null && !SortedModels.ContainsKey(-Model.ID))
                    SortedModels.Add(-Model.ID, Model);

            foreach (MM_Database_Model Model in SortedModels.Values)
            {
                ToolStripMenuItem NewItem = DropDownCollection[Model.ModelCategory] as ToolStripMenuItem;
                if (NewItem == null)
                {
                    NewItem = DropDownCollection.Add(Model.ModelCategory) as ToolStripMenuItem;
                    NewItem.Name = Model.ModelCategory;
                }

                //Now, locate our classification
                ToolStripMenuItem SecondItem = NewItem.DropDownItems[Model.ModelClass] as ToolStripMenuItem;
                if (SecondItem == null)
                {
                    SecondItem = NewItem.DropDownItems.Add(Model.ModelClass) as ToolStripMenuItem;
                    SecondItem.Name = Model.ModelClass;
                    if (EventHandler != null)
                        SecondItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(EventHandler);
                }
                if (String.IsNullOrEmpty(Model.FullClass))
                    SecondItem.DropDownItems.Add(Model.Name).Tag = Model;
                else
                    SecondItem.DropDownItems.Add(Model.FullClass).Tag = Model;
            }
        }
        #endregion



        /// <summary>
        /// Convert text so that the first letter of each word is capitalized
        /// </summary>
        /// <param name="InString"></param>
        /// <returns></returns>
        public string TitleCase(string InString)
        {
            if (InString == null)
                return null;
            char[] inArray = InString.ToCharArray();
            bool FirstLetter = true;
            for (int a = 0; a < inArray.Length; a++)
                if (char.IsSeparator(inArray[a]) || char.IsPunctuation(inArray[a]))
                    FirstLetter = true;
                else if (FirstLetter)
                {
                    inArray[a] = Char.ToUpper(inArray[a]);
                    FirstLetter = false;
                }
                else
                    inArray[a] = Char.ToLower(inArray[a]);
            return new string(inArray);
        }


        /// <summary>
        /// Mimic the AddWithValue function of Oracle and SqLite Command
        /// </summary>
        /// <param name="sCmd">The command</param>
        /// <param name="Name">The name of the parameter</param>
        /// <param name="Value">The value of the parameter</param>
        private void AddParameterWithValue(DbCommand sCmd, String Name, Object Value)
        {
            DbParameter OutParam = sCmd.CreateParameter();
            OutParam.ParameterName = Name;
            OutParam.Value = Value;
            sCmd.Parameters.Add(OutParam);
        }

        /// <summary>
        /// Create a connection with specified text.
        /// </summary>
        /// <param name="CommandText"></param>
        /// <param name="Connection"></param>
        /// <returns></returns>
        private DbCommand CreateCommand(String CommandText, DbConnection Connection)
        {
            DbCommand OutCmd = Connection.CreateCommand();
            OutCmd.CommandText = CommandText;
            return OutCmd;
        }


        /// <summary>
        /// Load in our substation latitudes/longitudes
        /// </summary>
        public void LoadCoordinates()
        {
            String FileName = Path.GetDirectoryName(CIMFileName) + "\\Coordinates.csv";
            using (StreamReader sRd = new StreamReader(FileName))
                while (!sRd.EndOfStream)
                {
                    string[] splStr = sRd.ReadLine().Split(',');
                    //if (splStr[0] != "Station")
                    //   SubstationDetails.Add(splStr[0], new KeyValuePair<string, PointF>(splStr[0], new PointF(Single.Parse(splStr[4]), Single.Parse(splStr[5]))));
                }
        }



        /// <summary>
        /// Add appropriate menu contingency menu items for a TEID
        /// </summary>
        /// <param name="cms"></param>
        /// <param name="Element"></param>
        public void AddContingencyMenuItems(System.Windows.Forms.ToolStripItemCollection cms, CIM_Element Element)
        {
            cms.Add("-");
            /* foreach (CIM_Contingency Ctg in LocateContingencies(Element))
             {
                 ToolStripMenuItem NewMenu = cms.Add(String.Format("{0} - {1}", Ctg.Name, Ctg.Description)) as ToolStripMenuItem;
                 NewMenu.Tag = new KeyValuePair<CIM_Contingency, CIM_Element>(Ctg, Element);
             }
         }

         /// <summary>
         /// Locate all contingencies associated with this element
         /// </summary>
         /// <param name="Element"></param>
         /// <returns></returns>
         private IEnumerable<CIM_Contingency> LocateContingencies(CIM_Element Element)
         {
             List<CIM_Contingency> OutCtg  = new List<CIM_Contingency>();
             foreach (CIM_Element Elem in Element.Parse("cim:ContingencyElement>cim:Contingency",Repository))
                 OutCtg.Add(Elem as CIM_Contingency);
             return OutCtg;*/
        }


        #region mRID updating
        /// <summary>
        /// Retrieve the last MRID
        /// </summary>
        /// <returns></returns>
        public int GetMRID()
        {
            return ++MaxMRID;
        }

        #endregion

        /// <summary>
        /// Determine the MM element type for a CIM element
        /// </summary>
        /// <param name="Elem"></param>
        /// <returns></returns>
        public MM_Element_Type LocateElementType(CIM_Element Elem)
        {
            foreach (MM_Element_Type ElemType in ElemTypes.Values)
                if (ValidateType(ElemType, Elem))
                    return ElemType;
            return null;
        }

        /// <summary>
        /// Determine whether a type is a match for the CIM element
        /// </summary>
        /// <param name="ElemType">The element type to search</param>
        /// <param name="SubElement">The CIM element to be searched</param>
        /// <returns></returns>
        public static bool ValidateType(MM_Element_Type ElemType, CIM_Element SubElement)
        {
            if (String.IsNullOrEmpty(ElemType.CIMName) || !SubElement.TypeName.EndsWith(ElemType.CIMName, StringComparison.CurrentCultureIgnoreCase))
                return false;
            else if (String.IsNullOrEmpty(ElemType.CIMParameter))
                return true;
            else
            {
                //First, retrieve the element's XML.
                String ElemXml = SubElement.XML;
                int MathPos = ElemType.CIMParameter.IndexOfAny(new char[] { '<', '>', '=', '!' });
                String AttributeName = ElemType.CIMParameter.Substring(0, MathPos);
                String ResultValue = ElemType.CIMParameter.Substring(MathPos + 1);
                Object AttributeValue;
                if (ResultValue == "null")
                    AttributeValue = null;
                else
                    AttributeValue = float.Parse(ResultValue);

                //First, determine if our attribute is present within the XML.
                Object OutVal = null;
                if (ElemXml.IndexOf(AttributeName + ">", StringComparison.CurrentCultureIgnoreCase) != -1)
                {
                    int XmlPos = ElemXml.IndexOf(AttributeName + ">", StringComparison.CurrentCultureIgnoreCase) + MathPos + 1;
                    OutVal = 0;
                    String CurrentValue = ElemXml.Substring(XmlPos, ElemXml.IndexOf('<', XmlPos) - XmlPos);
                    float floatVal;
                    float.TryParse(CurrentValue, out floatVal);
                    OutVal = floatVal;
                }
                else
                    foreach (CIM_Link Elem in SubElement.Links)
                        if (Elem.TypeName.Split(':')[1] == AttributeName)
                            OutVal = Elem.Element;

                if (ElemType.CIMParameter[MathPos] == '<')
                    return (float)OutVal < (float)AttributeValue;
                else if (ElemType.CIMParameter[MathPos] == '>')
                    return (float)OutVal > (float)AttributeValue;
                else if (ElemType.CIMParameter[MathPos] == '=')
                {
                    if (AttributeValue is Single)
                        return (float)OutVal == (float)AttributeValue;
                    else if (AttributeValue == null && OutVal == null)
                        return true;
                    else
                        return false;
                }
                else if (ElemType.CIMParameter[MathPos] == '!')
                {
                    if (AttributeValue is Single)
                        return (float)OutVal != (float)AttributeValue;
                    else if (AttributeValue == null ^ OutVal == null)
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
        }
        #region Activities
        /// <summary>
        /// Display a CIM browser around an element
        /// </summary>
        /// <param name="Elem">The element to show</param>
        public static void DisplayElement(CIM_Element Elem)
        {
            if (MM_Database_Model.DefaultTarget == null)
                Data_Manager.DisplayForm(typeof(frmCIMBrowser), "CIM Browser: " + Elem.ToString(), DockState.Document, Elem);
            else
                MM_Database_Model.DefaultTarget.AddElement(Elem, null);
        }
        #endregion

        #region IComparable<MM_Database_Model> Members
        /// <summary>
        /// Compare this model against another
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(MM_Database_Model other)
        {
            int DateCompare = ValidStart.CompareTo(other.ValidStart);
            if (DateCompare != 0)
                return DateCompare;
            else
                return Name.CompareTo(other.Name);
        }

        #endregion

        #region IComparable Members
        /// <summary>
        /// Compare this model to another one
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            if (obj is MM_Database_Model)
                return CompareTo(obj as MM_Database_Model);
            else
                return this.ToString().CompareTo(obj.ToString());
        }

        #endregion

        /// <summary>
        /// When possible, load in the summary table
        /// </summary>
        public void LoadSummaryTable()
        {
            try
            {
                using (SQLiteCommand sCmd = sConn.CreateCommand())
                {
                    sCmd.CommandText = "SELECT [Name],[Value] FROM Summary";
                    using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                        while (sRd.Read())
                            MM_Serializable.AssignValue(this, sRd["Name"].ToString(), sRd["Value"], null, null);
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Determine the coordinates for a substation
        /// </summary>
        /// <param name="ConnectedStation"></param>
        /// <returns></returns>
        public PointF GetCoordinates(CIM_Element ConnectedStation)
        {
            try
            {
                using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT Longitude, Latitude from MM_SUBSTATION WHERE rdfID=:0", ConnectedStation.rdfID.ToString()))
                using (DbDataReader dRd = dCmd.ExecuteReader())
                    if (dRd.Read())
                        if (dRd["Longitude"] is DBNull == false)
                            return new PointF(Convert.ToSingle(dRd["Longitude"]), Convert.ToSingle(dRd["Latitude"]));
                        else
                            return new PointF(float.NaN, float.NaN);
                    else
                        return new PointF(float.NaN, float.NaN);
            }
            catch
            {
                return new PointF(float.NaN, float.NaN);
            }
        }



        /// <summary>
        /// Load an incremental file, and add its components into our model.
        /// </summary>
        /// <param name="IncrementalUpdates"></param>
        /// <param name="RemoveTypes">Remove existing instances of the specified types</param>
        public void LoadIncrementalFile(XmlDocument IncrementalUpdates, bool RemoveTypes)
        {
            CIM_Element_Difference[] Diffs = CIM_Element_Difference.LoadDifferenceFile(IncrementalUpdates, this, RemoveTypes);
            foreach (CIM_Element_Difference cDiff in Diffs)
            {
                ModelChanges.Add(cDiff.rdfID, cDiff);
                if ((cDiff.ChangeType & CIM_Element_Difference.enumChangeType.ElementAddition) == CIM_Element_Difference.enumChangeType.ElementAddition && this is CIM_DirectModel)
                {
                    CIM_DirectModel thisD = this as CIM_DirectModel;
                    CIM_FakeElement NewFake = new CIM_FakeElement(cDiff.TypeName, cDiff.rdfID, cDiff.AddedAttributes, cDiff.AddedLinks.ToArray(), this);
                    thisD.Elements.Add(NewFake.rdfID, NewFake);
                    CIM_Element LinkedElement;
                    foreach (CIM_RdfID rdfID in cDiff.AddedLinks)
                        if (thisD.Elements.TryGetValue(rdfID, out LinkedElement))
                            if (LinkedElement is CIM_DirectElement)
                                (LinkedElement as CIM_DirectElement)._InLinks.Add(NewFake.ID);
                            else if (LinkedElement is CIM_FakeElement)
                                (LinkedElement as CIM_FakeElement)._InLinks.Add(NewFake.ID);
                }
            }

            Substations.Clear();
            Lines.Clear();
            Companies.Clear();
            LoadCompanies();
            LoadSubstationsFromCIM();
            LoadLinesFromCIM();
            this.State = enumModelState.Completed;
            
        }

        /// <summary>
        /// Write out our model with incremental updates
        /// </summary>
        /// <param name="TargetFile"></param>
        public void WriteModelWithIncrementalUpdates(String TargetFile)
        {           
            using (StreamWriter sW = new StreamWriter(TargetFile, false, new UTF8Encoding(false)))
            {
                sW.WriteLine("<?xml version=\"1.0\"?>");
                sW.WriteLine("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:cim=\"http://iec.ch/TC57/2006/CIM-schema-cim10#\" xmlns:spc=\"http://www.siemens-ptd/SHIMM1.0#\" xmlns:etx=\"http://www.ercot.com/CIM11R0/2008/2.0/extension#\">");                
                foreach (String ElemToExport in Types)
                    foreach (CIM_Element Elem in ElementsOfType(ElemToExport))
                        sW.WriteLine(Elem.XML);
                sW.WriteLine("</rdf:RDF>");
            }
        }
        
    }
}
